﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace CodeBetter.Extensions
{
   public static class XmlWriterExtensions
   {
      public abstract class XmlWriterWatcher : IDisposable
      {
         protected XmlWriter writer;
         private bool disposed = false;

         public XmlWriterWatcher(XmlWriter writer)
         {
            this.writer = writer;
         }

         protected abstract void CallWriteEndMethod();

         #region IDisposable Members

         public void Dispose()
         {
            Dispose(true);
            GC.SuppressFinalize(this);
         }

         public void Dispose(bool disposing)
         {
            if (!this.disposed)
            {
               // If disposing equals true, dispose all managed and unmanaged resources.
               if (disposing)
               {
                  CallWriteEndMethod();
                  writer = null;
               }
               // Call the appropriate methods to clean up unmanaged resources here.
            }
            disposed = true;
         }

         #endregion

         ~XmlWriterWatcher()
         {
            Dispose(false);
         }
      }

      public class StartElementWatcher : XmlWriterWatcher
      {
         private bool useWriteFullElementEnd = false;

         public StartElementWatcher(XmlWriter writer)
            : base(writer)
         {
         }

         public StartElementWatcher(XmlWriter writer, bool useWriteFullElementEnd)
            : base(writer)
         {
            this.useWriteFullElementEnd = useWriteFullElementEnd;
         }

         protected override void CallWriteEndMethod()
         {
            if (useWriteFullElementEnd)
               writer.WriteFullEndElement();
            else
               writer.WriteEndElement();
         }

      }

      public class StartDocumentWatcher : XmlWriterWatcher
      {
         public StartDocumentWatcher(XmlWriter writer)
            : base(writer)
         {
         }

         protected override void CallWriteEndMethod()
         {
            writer.WriteEndDocument();
         }
      }

      public class StartAttributeWatcher : XmlWriterWatcher
      {
         public StartAttributeWatcher(XmlWriter writer)
            : base(writer)
         {
         }

         protected override void CallWriteEndMethod()
         {
            writer.WriteEndAttribute();
         }
      }

      /// <summary>
      /// Writes out a start tag with the specified local name and returns a StartElementWatcher object of which the sole
      /// purpose it to write out the end tag when disposed.
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="localName">The local name of the element.</param>
      /// <returns>StartElementWatcher which when disposed calls WriteEndElement on the XmlWriter.</returns>
      public static StartElementWatcher StartElement(this XmlWriter writer, string localName)
      {
         writer.WriteStartElement(localName);
         return new StartElementWatcher(writer);
      }

      /// <summary>
      /// Writes out the specified start tag and associates it with the given namespace.
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="localName">The local name of the element.</param>
      /// <param name="ns">The namespace URI to associate with the element. If this namespace is already in 
      /// scope and has an associated prefix, the writer writes that prefix also.</param>
      /// <returns></returns>
      public static StartElementWatcher StartElement(this XmlWriter writer, string localName, string ns)
      {
         writer.WriteStartElement(localName, ns);
         return new StartElementWatcher(writer);
      }

      /// <summary>
      /// Writes out the specified start tag and associates it with the given namespace and prefix.
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="prefix">The namespace prefix for the element.</param>
      /// <param name="localName">The local name of the element.</param>
      /// <param name="ns">The namespace URI to associate with the element.</param>
      /// <returns></returns>
      public static StartElementWatcher StartElement(this XmlWriter writer, string prefix, string localName, string ns)
      {
         writer.WriteStartElement(prefix, localName, ns);
         return new StartElementWatcher(writer);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="localName">The local name of the element.</param>
      /// <param name="useWriteFullEndElement"></param>
      /// <returns></returns>
      public static StartElementWatcher StartElement(this XmlWriter writer, string localName, bool useWriteFullEndElement)
      {
         writer.WriteStartElement(localName);
         return new StartElementWatcher(writer, useWriteFullEndElement);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="localName">The local name of the element.</param>
      /// <param name="ns"></param>
      /// <param name="useWriteFullEndElement"></param>
      /// <returns></returns>
      public static StartElementWatcher StartElement(this XmlWriter writer, string localName, string ns, bool useWriteFullEndElement)
      {
         writer.WriteStartElement(localName, ns);
         return new StartElementWatcher(writer, useWriteFullEndElement);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="prefix"></param>
      /// <param name="localName">The local name of the element.</param>
      /// <param name="ns"></param>
      /// <param name="useWriteFullEndElement"></param>
      /// <returns></returns>
      public static StartElementWatcher StartElement(this XmlWriter writer, string prefix, string localName, string ns, bool useWriteFullEndElement)
      {
         writer.WriteStartElement(prefix, localName, ns);
         return new StartElementWatcher(writer, useWriteFullEndElement);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <param name="standalone">If true, it writes "standalone=yes", if false, it writes "standalone=no"</param>
      /// <returns></returns>
      public static StartDocumentWatcher StartDocument(this XmlWriter writer, bool standalone)
      {
         writer.WriteStartDocument(standalone);
         return new StartDocumentWatcher(writer);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer">The XmlWriter this extension method is going to be called on.</param>
      /// <returns></returns>
      public static StartDocumentWatcher StartDocument(this XmlWriter writer)
      {
         writer.WriteStartDocument();
         return new StartDocumentWatcher(writer);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer"></param>
      /// <param name="localName"></param>
      /// <returns></returns>
      public static StartAttributeWatcher StartAttribute(this XmlWriter writer, string localName)
      {
         writer.WriteStartAttribute(localName);
         return new StartAttributeWatcher(writer);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer"></param>
      /// <param name="localName"></param>
      /// <param name="ns"></param>
      /// <returns></returns>
      public static StartAttributeWatcher StartAttribute(this XmlWriter writer, string localName, string ns)
      {
         writer.WriteStartAttribute(localName, ns);
         return new StartAttributeWatcher(writer);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="writer"></param>
      /// <param name="prefix"></param>
      /// <param name="localName"></param>
      /// <param name="ns"></param>
      /// <returns></returns>
      public static StartAttributeWatcher StartAttribute(this XmlWriter writer, string prefix, string localName, string ns)
      {
         writer.WriteStartAttribute(prefix, localName, ns);
         return new StartAttributeWatcher(writer);
      }
   }
}
